#pragma once 
#include "Mutex.hpp"
#include <string>
#include <filesystem>
#include <fstream>
#include <memory>
#include <sstream>
#include <unistd.h>
namespace MyLogModule
{
    using namespace MyMutexModule;
    const string defaultlogpath = "./log/";
    const string defaultlogname = "log.txt";
    class Logstrategy
    {
    public:
        Logstrategy() = default;
        ~Logstrategy()= default;
        virtual void SyncOutput(const string& message) = 0;
    };
    class ConsoleLogStrategy : public Logstrategy
    {
    public:
        ConsoleLogStrategy(){}
        virtual void SyncOutput(const string& message){
            LockGuard lock(_mutex);
            cout << message << endl;
        }
    private:
        Mutex _mutex;
    };
    class FileLogStrategy : public Logstrategy
    {
    public:
        FileLogStrategy(const string& logpath = defaultlogpath,const string& logname = defaultlogname)
            :_logname(logname),_logpath(logpath)
        {
            LockGuard lock(_mutex);
            if (filesystem::exists(_logpath)){
                return;
            }
            try
            {
                std::filesystem::create_directories(_logpath);
            }
            catch(std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << '\n';
                return;
            }
            
        }
        virtual void SyncOutput(const string& message){
            LockGuard lock(_mutex);
            string file = _logpath + _logname;
            fstream f(file,std::ios::app);
            if (!f.is_open()){
                cout << "文件没有打开" << endl;
                return;
            }
            f << message << endl;
        }
    private:
        Mutex _mutex;
        string _logpath;
        string _logname;        
    };
    enum  LogLevel {
        DEBUG = 1, //调试信息
        INFO,      //正常显示
        WARNING,   //警告信息，不影响程序执行
        ERROR,     //错误信息
        FATAL      //致命错误，程序终止
    };
    //把日志等级转为如下格式
    std::string Level2string(const LogLevel& level){
        switch(level){
            case LogLevel::DEBUG:
                return "DEBUG";
            case LogLevel::INFO:
                return "INFO";
            case LogLevel::WARNING:
                return "WARNING";
            case LogLevel::ERROR:
                return "ERROR";
            case LogLevel::FATAL:
                return "FATAL";              
            default:
                return "NONE";                                                  
        }
    }    
    string GetCurTime(){
        char buffer[128];
        time_t t = time(nullptr);
        struct tm curr;
        localtime_r(&t,&curr);
        snprintf(buffer,sizeof(buffer),"%4d-%2d-%2d %02d:%02d:%02d",
            curr.tm_year + 1900,
            curr.tm_mon + 1,
            curr.tm_mday,
            curr.tm_hour,
            curr.tm_min,curr.tm_sec
        );
        return buffer;
    }
    class Logger
    {
    public:
        Logger(){
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableConsoleLogStrategy(){
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableFileLogStrategy(){
            _strategy = std::make_shared<FileLogStrategy>();
        }
        //内部类
        class LogMessage{
        public:
            LogMessage(const string& filename,int line,LogLevel level,Logger& logger)
                :_curtime(GetCurTime()),
                _logger(logger),
                _level(level),
                _line(line),
                _filename(filename)
            {
                stringstream ss;
                ss << "[" << _curtime << "]",
                ss << "[" << Level2string(_level) << "]",
                ss << "[" << getpid() << "]",
                ss << "[" << filename << "]",
                ss << "[" << line << "] -";
                _loginfo = ss.str();
            }

            template<class T>
            LogMessage& operator<<(const T&val){
                stringstream ss;
                ss << val;
                _loginfo += ss.str();
                return *this;
            } 
            ~LogMessage(){
                _logger._strategy->SyncOutput(_loginfo);
            }
        private:
            string _curtime;
            string _loginfo;
            Logger& _logger;
            LogLevel _level;
            int _line;
            string _filename;
        };
        LogMessage operator()(const string& filename,int line,LogLevel level){
            return LogMessage(filename,line,level,*this);
        }
    private:
        std::shared_ptr<Logstrategy> _strategy;
    };
    Logger logger;
    #define LOG(LEVEL) logger(__FILE__,__LINE__,LEVEL)
    #define ENABLECONSOLESTRATEGY() logger.EnableConsoleLogStrategy()
    #define ENABLEFILESTRATEGY() logger.EnableFileLogStrategy();

}